﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Objects;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;


namespace EntityFrameWorkDAL
{
    public class Repository<T> where T : class, new()
    {

        public DbContext Context = null;
        public ObjectContext ObjectContext = null;

        public DbSet<T> Datas { get { return Context.Set<T>(); } }

        public Repository(DbContext context)
        {
            Context = context;
            ObjectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)Context).ObjectContext;
        }

        public int Add(T model)
        {
            Datas.Add(model);
            return Context.SaveChanges();
        }
        public int Add(IEnumerable<T> list)
        {
            Datas.AddRange(list);
            return Context.SaveChanges();
        }

        public virtual int Update(T model)
        {
            var entity = Context.Entry<T>(model);
            if (entity.State == EntityState.Detached)
            {
                try
                {
                    Datas.Attach(model);
                    entity.State = EntityState.Modified;
                }
                catch (InvalidOperationException)
                {
                    var key = ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Unchanged | EntityState.Modified | EntityState.Added | EntityState.Deleted).First().EntityKey;
                    List<object> ids = new List<object>();
                    foreach(var i in key.EntityKeyValues){
                        var id = typeof(T).GetProperty(i.Key).GetValue(model);
                        ids.Add(id);
                    }
                    var oldmodel=Datas.Find(ids.ToArray());
                    Context.Entry(oldmodel).CurrentValues.SetValues(model);
                }
            }
            return Context.SaveChanges(); ;
        }

        public virtual int Update(IEnumerable<T> list)
        {
            return Context.SaveChanges();
        }

        #region 删除
        public virtual int Delete(T model)
        {
            Datas.Remove(model);
            return Context.SaveChanges();
        }

        public virtual int Delete(IEnumerable<T> dellist)
        {
            Datas.RemoveRange(dellist);
            return Context.SaveChanges();
        }


        /// <summary>
        /// 删除 
        /// Delete("it.Test like '%a%'"),Delete("it.Test like @a",new ObjectParameter("a","%a%"))
        /// </summary>
        /// <param name="where">it.Test like '%a%' it.Test like @a</param>
        /// <param name="parameters">参数，where中的以@开关的参数， new ObjectParameter("a","%a%"))</param>
        public virtual int Delete(string where, params ObjectParameter[] parameters)
        {
            int? cnt = 0;
            var dellist = Search(where, null, null, null, ref cnt, parameters);
            return Delete(dellist);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="where">r=>r.test=="a";
        /// Common.LambdaExp.True<Model.DataLayer>().And(r => r.id==2)</param>
        public virtual int Delete(Expression<Func<T, bool>> where)
        {
            int? cnt = 0;
            var dellist= Search(where, null, null, null, ref cnt);
            return Delete(dellist);
        }

        #endregion

        #region 字段串形式搜索

        /// <summary>
        /// 搜索 
        /// Search("it.Test like '%a%'"),Search("it.Test like @a",new ObjectParameter("a","%a%"))
        /// </summary>
        /// <param name="where">it.Test like '%a%' it.Test like @a</param>
        /// <param name="parameters">参数，where中的以@开关的参数， new ObjectParameter("a","%a%"))</param>
        /// <returns></returns>
        public virtual IQueryable<T> Search(string where, params ObjectParameter[] parameters)
        {
            int? cnt = 0;
            return Search(where, null, null, null, ref cnt, parameters);
        }

        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="where">it.Test like '%a%' it.Test like @a</param>
        /// <param name="orderbystr">排序字段,与SQL排序用法一至</param>
        /// <param name="parameters">参数，where中的以@开关的参数， new ObjectParameter("a","%a%"))</param>
        /// <returns></returns>
        public virtual IQueryable<T> Search(string where, string orderbystr, params ObjectParameter[] parameters)
        {
            int? cnt = 0;
            return Search(where, orderbystr, null, null, ref cnt, parameters);
        }

        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="where">it.Test like '%a%' it.Test like @a</param>
        /// <param name="orderbystr">排序字段,与SQL排序用法一至</param>
        /// <param name="start">数据起始位置，以0开始</param>
        /// <param name="length">限制返回数据个数，0不限制</param>
        /// <param name="allCount">空值返回当前查询总数，非空不返回</param>
        /// <param name="parameters">参数，where中的以@开关的参数， new ObjectParameter("a","%a%"))</param>
        /// <returns></returns>
        public virtual IQueryable<T> Search(string where, string orderbystr, int? start, int? length, ref int? allCount, params ObjectParameter[] parameters)
        {
            IQueryable<T> query = ObjectContext.CreateObjectSet<T>();
            if (!string.IsNullOrWhiteSpace(where))
            {
                query = (query as System.Data.Entity.Core.Objects.ObjectSet<T>).Where(where, parameters);
            }
            query = Page(query, orderbystr, start, length, ref allCount);
            return query;
        }
        #endregion

        #region Lambda形式搜索

        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="where">r=>r.test=="a";
        /// Common.LambdaExp.True<Model.DataLayer>().And(r => r.id==2)</param>
        public virtual IQueryable<T> Search(Expression<Func<T, bool>> where)
        {
            int? cnt = 0;
            return Search(where, null, null, null, ref cnt);
        }

        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="where">r=>r.test=="a";
        /// Common.LambdaExp.True<Model.DataLayer>().And(r => r.id==2)</param>
        /// <param name="orderbystr">排序字段,与SQL排序用法一至</param>
        /// <returns></returns>
        public virtual IQueryable<T> Search(Expression<Func<T, bool>> where, string orderbystr)
        {
            int? cnt = 0;
            return Search(where, orderbystr, null, null, ref cnt);
        }

        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="where">r=>r.test=="a";
        /// Common.LambdaExp.True<Model.DataLayer>().And(r => r.id==2)</param>
        /// <param name="orderbystr">排序字段,与SQL排序用法一至</param>
        /// <param name="start">数据起始位置，以0开始</param>
        /// <param name="length">限制返回数据个数，0不限制</param>
        /// <param name="allCount">空值返回当前查询总数，非空不返回</param>
        /// <param name="parameters">参数，where中的以@开关的参数， new ObjectParameter("a","%a%"))</param>
        /// <returns></returns>
        public virtual IQueryable<T> Search(Expression<Func<T, bool>> where, string orderbystr, int? start, int? length, ref int? allCount)
        {
            IQueryable<T> query = Datas;
            if (where != null) query = query.Where(where);
            query = Page(query, orderbystr, start, length, ref allCount);
            return query;
        }

        #endregion

        #region 分页

        public virtual IQueryable<T> Page(IQueryable<T> query, string orderbystr, int? start, int? length)
        {
            int? cnt = 0;
            return Page(query, orderbystr, start, length, ref cnt);
        }


        /// <summary>
        /// 搜索，示例：Search(query, "Create,Code desc,ID asc", 0, 20,null)
        /// </summary>
        public virtual IQueryable<T> Page(IQueryable<T> query, string orderbystr, int? start, int? length, ref int? allCount)
        {
            if (allCount == null) allCount = query.Count();
            if (!string.IsNullOrWhiteSpace(orderbystr))
            {
                #region 动态组建排序方式
                var orderbys = orderbystr.Split(',');               
                for (var i = 0; i < orderbys.Length; i++)
                {
                    var orderby = orderbys[i];
                    var ts = orderby.Split(' ');
                    var filed = ts[0];                    
                    string sort = null;
                    if (ts.Length > 1 && ts[1].ToLower() == "desc")
                    {
                        sort = i == 0 ? "OrderByDescending" : "ThenByDescending";
                    }
                    else
                    {
                        sort = i == 0 ? "OrderBy" : "ThenBy";
                    }
                    var fs = filed.Split('.');
                    var type = typeof(T);
                    var parameter = Expression.Parameter(typeof(T), "o");
                    var property = type.GetProperty(fs[0]);
                    var propertyAccess = Expression.MakeMemberAccess(parameter, property);         
                    if (fs.Length > 1)
                    {
                        for(var j = 1; j < fs.Length; j++)
                        {
                             property = property.PropertyType.GetProperty(fs[j]);
                             propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                        }
                    }
                    var orderByExp = Expression.Lambda(propertyAccess, parameter);
                    var types = new Type[] { type, property.PropertyType };
                    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), sort, types, query.Expression, Expression.Quote(orderByExp));
                    query = query.Provider.CreateQuery<T>(resultExp);
                }
                #endregion
            }
            if (start != null && start.Value > 0)
            {
                if (string.IsNullOrWhiteSpace(orderbystr)) throw new Exception("指定了start参数时orderbystr不能为空");
                query = query.Skip(start.Value);
            }
            if (length != null && length.Value > 0)
            {
                query = query.Take(length.Value);
            }
            return query;
        }

        #endregion

    }
}
